package jhx;

/**
 * Double Linked List (with circular iterator)
 * 
 * coded for haxe-2.06 and neko-1.8.1/hxcpp-2.06.1
 *
 * developed over LList 1.0 and for jhxlib
 * has not yet been properly tested and/or optimized
 * 
 * @author Jonas Malaco Filho
 * @version 1.02
 * 
 * Copyright (c) 2010 Jonas Tadeu Silva Malaco Filho <jonasmalacofilho@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */

class DLList<T>
{
	public var head(default, null):DLListNode<T>;
	public var tailEnd(default, null):DLListNode<T>;
	public var length(default, null):Int;
	
	public function new() 
	{
		head = null;
		tailEnd = null;
		length = 0;
	}
	
	/**
	 * Pushs an object to the start of the list
	 */
	public inline function push(__list__obj:T)
	{
		switch (head) {
			case null: 
				tailEnd = head = new DLListNode<T>(__list__obj, null, null);
			default: 
				head = head.previous = new DLListNode<T>(__list__obj, null, head);
		}
		length++;
	}
	
	public inline function add(__list__obj:T)
	{
		switch (tailEnd) {
			case null: 
				head = tailEnd = new DLListNode<T>(__list__obj, null, null);
			default: 
				tailEnd = tailEnd.next = new DLListNode<T>(__list__obj, tailEnd, null);
		}
		length++;
	}
	
	public inline function mpush(__list__obj:T)
	{
		switch (head) {
			case null: 
				tailEnd = head = new DLListNode<T>(__list__obj, null, null);
			default: 
				head = head.previous = new DLListNode<T>(__list__obj, null, head);
		}
		length++;
		return head;
	}
	
	public inline function madd(__list__obj:T)
	{
		switch (tailEnd) {
			case null: 
				head = tailEnd = new DLListNode<T>(__list__obj, null, null);
			default: 
				tailEnd = tailEnd.next = new DLListNode<T>(__list__obj, tailEnd, null);
		}
		length++;
		return tailEnd;
	}
	
	public inline function first()
	{
		return switch (head) {
			case null:
				null;
			default:
				head.obj;
		};
	}
	
	public inline function mfirst()
	{
		return head;
	}
	
	public inline function isEmpty()
	{
		return switch (head) {
			case null:
				true;
			default:
				false;
		};
	}
	
	public inline function clear()
	{
		head = null;
		tailEnd = null;
		length = 0;
	}
	
	/**
	 * Pops the first element of the list
	 */
	public inline function pop()
	{
		return switch (head) {
			case null:
				null;
			default:
				var obj = head.obj;
				if (head.next != null) {
					head.next.previous = null;
					head = head.next;
				}
				else {
					head = tailEnd = null;
				}
				length--;
				obj;
		};
	}
	
	public function remove(obj:T)
	{
		var current = head;
		var sucess = false;
		while (
			switch (current) {
				case null: false;
				default:
					if (current.obj == obj) {
						var killedPointers = current.mkill();
						if (killedPointers.pre != null) {
							killedPointers.pre.next = killedPointers.nx;
						} else {
							head = killedPointers.nx;
						}
						if (killedPointers.nx != null) {
							killedPointers.nx.previous = killedPointers.pre;
						} else {
							tailEnd = killedPointers.pre;
						}
						length--;
						sucess = true;
						false;
					}
					else {
						current = current.next;
						true;
					}
			}
		) { }
		return sucess;
	}
	
	public inline function mremove(__list__node:DLListNode<T>)
	{
		return switch (__list__node) {
			case null: false;
			default:
				var __list__kptrs = __list__node.mkill();
				if (__list__kptrs.pre != null) {
					__list__kptrs.pre.next = __list__kptrs.nx;
				} else {
					head = __list__kptrs.nx;
				}
				if (__list__kptrs.nx != null) {
					__list__kptrs.nx.previous = __list__kptrs.pre;
				} else {
					tailEnd = __list__kptrs.pre;
				}
				length--;
				true;
		}
	}
	
	public inline function insertAfter(__list__obj:T, __list__ref:DLListNode<T>)
	{
		switch (__list__ref) {
			case null:
			default:
				length++;
				if (__list__ref.next != null) {
					__list__ref.next = __list__ref.next.previous = new DLListNode<T>(__list__obj, __list__ref, __list__ref.next);
				} else {
					tailEnd = __list__ref.next = new DLListNode<T>(__list__obj, __list__ref, __list__ref.next);
				}
		};
	}
	
	public inline function minsertAfter(__list__obj:T, __list__ref:DLListNode<T>)
	{
		return switch (__list__ref) {
			case null:
				null;
			default:
				length++;
				if (__list__ref.next != null) {
					__list__ref.next = __list__ref.next.previous = new DLListNode<T>(__list__obj, __list__ref, __list__ref.next);
				} else {
					tailEnd = __list__ref.next = new DLListNode<T>(__list__obj, __list__ref, __list__ref.next);
				}
		};
	}
	
	public inline function insertBefore(__list__obj:T, __list__ref:DLListNode<T>)
	{
		switch (__list__ref) {
			case null:
			default:
				length++;
				if (__list__ref.previous != null) {
					__list__ref.previous = __list__ref.previous.next = new DLListNode<T>(__list__obj, __list__ref.previous, __list__ref);
				} else {
					head = __list__ref.previous = new DLListNode<T>(__list__obj, __list__ref.previous, __list__ref);
				}
		};
	}
	
	public inline function minsertBefore(__list__obj:T, __list__ref:DLListNode<T>)
	{
		return switch (__list__ref) {
			case null:
				null;
			default:
				length++;
				if (__list__ref.previous != null) {
					__list__ref.previous = __list__ref.previous.next = new DLListNode<T>(__list__obj, __list__ref.previous, __list__ref);
				} else {
					head = __list__ref.previous = new DLListNode<T>(__list__obj, __list__ref.previous, __list__ref);
				}
		};
	}
	
	public inline function replace(__list__obj:T, __list__ref:DLListNode<T>)
	{
		switch (__list__ref) {
			case null:
				null;
			default:
				__list__ref.obj = __list__obj;
		};
	}
	
	public function mfind(obj:T)
	{
		var current = head;
		while (
			switch (current) {
				case null:
					current = null;
					false;
				default:
					if (current.obj == obj) {
						false;
					}
					else {
						current = current.next;
						true;
					}
			}
		) { }
		return current;
	}
	
	public function join(sep:String)
	{
		var s = new StringBuf();
		var first = true;
		var cur = head;
		while(cur != null) {
			if(first)
				first = false;
			else
				s.add(sep);
			s.add(Std.string(cur.obj));
			cur = cur.next;
		}
		return s.toString();
	}
	
	public function cjoin(sep:String)
	{
		var s = new StringBuf();
		var first = true;
		var cur = head;
		while(cur != null) {
			if(first)
				first = false;
			else
				s.add(sep);
			s.add(Std.string(cur.obj));
			cur = cur.next;
		}
		s.add(sep);
		s.add(Std.string(head.obj));
		return s.toString();
	}
	
	public function rjoin(sep:String)
	{
		var s = new StringBuf();
		var first = true;
		var cur = tailEnd;
		while(cur != null) {
			if(first)
				first = false;
			else
				s.add(sep);
			s.add(Std.string(cur.obj));
			cur = cur.previous;
		}
		return s.toString();
	}
	
	public function crjoin(sep:String)
	{
		var s = new StringBuf();
		var first = true;
		var cur = tailEnd;
		while(cur != null) {
			if(first)
				first = false;
			else
				s.add(sep);
			s.add(Std.string(cur.obj));
			cur = cur.previous;
		}
		s.add(sep);
		s.add(Std.string(tailEnd.obj));
		return s.toString();
	}
	
	public function toString()
	{
		var s = new StringBuf();
		var first = true;
		var cur = head;
		s.add("[" + Std.string(length) + "] {");
		while(cur != null) {
			if(first)
				first = false;
			else
				s.add(", ");
			s.add(Std.string(cur.obj));
			cur = cur.next;
		}
		s.add("}");
		return s.toString();
	}
	
	public function ctoString()
	{
		var s = new StringBuf();
		var first = true;
		var cur = head;
		s.add("[" + Std.string(length) + "] {");
		while(cur != null) {
			if(first)
				first = false;
			else
				s.add(", ");
			s.add(Std.string(cur.obj));
			cur = cur.next;
		}
		s.add(", ");
		s.add(Std.string(head.obj));
		s.add("}");
		return s.toString();
	}
	
	public static function rlist<A>(it:Iterable<A>)
	{
		var a = new DLList<A>();
		for (i in it) {
			switch (a.head) {
				case null: 
					a.tailEnd = a.head = new DLListNode<A>(i, null, null);
				default: 
					a.head = a.head.previous = new DLListNode<A>(i, null, a.head);
			}
			a.length++;
		}
		return a;
	}
	
	public static function list<A>(it:Iterable<A>)
	{
		var a = new DLList<A>();
		for (i in it) {
			switch (a.tailEnd) {
				case null: 
					a.head = a.tailEnd = new DLListNode<A>(i, null, null);
				default: 
					a.tailEnd = a.tailEnd.next = new DLListNode<A>(i, a.tailEnd, null);
			}
			a.length++;
		}
		return a;
	}
	
	public static function map<A, B>(it:Iterable<A>, f:A->B)
	{
		var a = new DLList<B>();
		for (i in it) {
			switch (a.tailEnd) {
				case null: 
					a.head = a.tailEnd = new DLListNode<B>(f(i), null, null);
				default: 
					a.tailEnd = a.tailEnd.next = new DLListNode<B>(f(i), a.tailEnd, null);
			}
			a.length++;
		}
		return a;
	}
	
	public function omap<B>(f:T->B)
	{
		var a = new DLList<B>();
		var cur = head;
		while (cur != null) {
			switch (a.tailEnd) {
				case null: 
					a.head = a.tailEnd = new DLListNode<B>(f(cur.obj), null, null);
				default: 
					a.tailEnd = a.tailEnd.next = new DLListNode<B>(f(cur.obj), a.tailEnd, null);
			}
			a.length++;
			cur = cur.next;
		}
		return a;
	}
	
	public static function mapi<A, B>(it:Iterable<A>, f:Int->A->B)
	{
		var a = new DLList<B>();
		var j = 0;
		for (i in it) {
			switch (a.tailEnd) {
				case null: 
					a.head = a.tailEnd = new DLListNode<B>(f(j++, i), null, null);
				default: 
					a.tailEnd = a.tailEnd.next = new DLListNode<B>(f(j++, i), a.tailEnd, null);
			}
			a.length++;
		}
		return a;
	}
	
	public function omapi<B>(f:Int->T->B)
	{
		var a = new DLList<B>();
		var cur = head;
		var j = 0;
		while (cur != null) {
			switch (a.tailEnd) {
				case null: 
					a.head = a.tailEnd = new DLListNode<B>(f(j++, cur.obj), null, null);
				default: 
					a.tailEnd = a.tailEnd.next = new DLListNode<B>(f(j++, cur.obj), a.tailEnd, null);
			}
			cur = cur.next;
		}
		a.length = j;
		return a;
	}
	
	/**
	 * Fold
	 */
	public function fold<B>(f:T->B->B, first:B)
	{
		var cur = head;
		while (cur != null) {
			first = f(cur.obj, first);
			cur = cur.next;
		}
		return first;
	}
	
	/**
	 * Circular fold
	 */
	public function cfold<B>(f:T->B->B, first:B)
	{
		var cur = head;
		while (cur != null) {
			first = f(cur.obj, first);
			cur = cur.next;
		}
		first = f(head.obj, first);
		return first;
	}
	
	/**
	 * Iterate
	 */
	public function iter(f:T->Void)
	{
		var cur = head;
		while (cur != null) {
			f(cur.obj);
			cur = cur.next;
		}
	}
	
	/**
	 * Ciruclar iterate
	 */
	public function citer(f:T->Void)
	{
		var cur = head;
		while (cur != null) {
			f(cur.obj);
			cur = cur.next;
		}
		f(head.obj);
	}
	
	/**
	 * Internal manipulation iterate
	 */
	public function miter(f:DLListNode<T>->DLListNode<T>->DLListNode<T>->Void)
	{
		var cur = head;
		while (cur != null) {
			f(cur, cur.previous, cur.next);
			cur = cur.next;
		}
	}
	
	/**
	 * Circular internal manipulation iterate
	 */
	public function cmiter(f:DLListNode<T>->DLListNode<T>->DLListNode<T>->Void)
	{
		var cur = head;
		while (cur != null) {
			if (cur.next != null) {
				if (cur.previous != null) {
					f(cur, cur.previous, cur.next);
				} else {
					f(cur, head, cur.next);
				}
			}
			else {
				if (cur.previous != null) {
					f(cur, cur.previous, head);
				} else {
					f(cur, head, head);
				}
			}
			cur = cur.next;
		}
	}
	
	/**
	 * Returns a clone for this list - Objects <T> are cloned with Reflection
	 */
	public function iclone()
	{
		var a = new DLList<T>();
		var cur = head;
		while (cur != null) {
			switch (a.tailEnd) {
				case null: 
					a.head = a.tailEnd = new DLListNode<T>(Reflect.copy(cur.obj), null, null);
				default: 
					a.tailEnd = a.tailEnd.next = new DLListNode<T>(Reflect.copy(cur.obj), a.tailEnd, null);
			}
			a.length++;
			cur = cur.next;
		}
		return a;
	}
	
	/**
	 * Returns a copy of the list structure - optimized list(this)
	 */
	public function copy()
	{
		var a = new DLList<T>();
		var cur = head;
		while (cur != null) {
			switch (a.tailEnd) {
				case null: 
					a.head = a.tailEnd = new DLListNode<T>(cur.obj, null, null);
				default: 
					a.tailEnd = a.tailEnd.next = new DLListNode<T>(cur.obj, a.tailEnd, null);
			}
			a.length++;
			cur = cur.next;
		}
		return a;
	}
	
	/**
	 * Returns a reverse list for this one
	 */
	public function rev()
	{
		var a = new DLList<T>();
		var cur = head;
		while (cur != null) {
			switch (a.head) {
				case null: 
					a.tailEnd = a.head = new DLListNode<T>(cur.obj, null, null);
				default: 
					a.head = a.head.previous = new DLListNode<T>(cur.obj, null, a.head);
			}
			a.length++;
			cur = cur.next;
		}
		return a;
	}
	
	public function filter(f:T->Bool)
	{
		var a = new DLList<T>();
		var cur = head;
		while (cur != null) {
			if (f(cur.obj)) {
				switch (a.tailEnd) {
					case null: 
						a.head = a.tailEnd = new DLListNode<T>(cur.obj, null, null);
					default: 
						a.tailEnd = a.tailEnd.next = new DLListNode<T>(cur.obj, a.tailEnd, null);
				}
				a.length++;
			}
			cur = cur.next;
		}
		return a;
	}
	
	/**
	 * Concatenate (destroys previous lists)
	 * list1, list2 -> list(1-2)
	 */
	public function concat(list:DLList<T>)
	{
		var a = new DLList<T>();
		if (list.head != null) {
			if (head != null) {
				a.head = head;
				tailEnd.next = list.head;
				list.head.previous = tailEnd;
				a.tailEnd = list.tailEnd;
				a.length = length + list.length;
				head = null; tailEnd = null; length = 0;
				list.head = null; list.tailEnd = null; length = 0;
			}
			else {
				a.head = list.head;
				a.tailEnd = list.tailEnd;
				a.length = list.length;
				list.head = null; list.tailEnd = null; length = 0;
			}
		}
		else {
				a.head = head;
				a.tailEnd = tailEnd;
				a.length = length;
				head = null; tailEnd = null; length = 0;
		}
		return a;
	}
	
#if cpp
	public function iterator():Iterator<T>
	{
		return new DLListIterator<T>(head);
	}
	public function riterator():Iterator<T>
	{
		return new DLListRIterator<T>(head);
	}
	public function citerator():Iterator<T>
	{
		return new DLListCIterator<T>(head);
	}
	public function criterator():Iterator<T>
	{
		return new DLListCRIterator<T>(head);
	}
#else
	public function iterator():Iterator<T>
	{
		var next = head;
		return {
			hasNext: function() { return next != null; },
			next: function() {
				var obj = next.obj;
				next = next.next;
				return obj;
			}
		};
	}
	public function riterator():Iterator<T>
	{
		var next = tailEnd;
		return {
			hasNext: function() { return next != null; },
			next: function() {
				var obj = next.obj;
				next = next.previous;
				return obj;
			}
		};
	}
	public function citerator():Iterator<T>
	{
		var myHead = head;
		var next = head;
		var circular = false;
		return {
			hasNext: function() {
				return (next != null || !circular);
			},
			next: function() {
				return switch (next) {
					case null:
						circular = true;
						switch (myHead) {
							case null: null;
							default: myHead.obj;
						}
					default:
						var obj = next.obj;
						next = next.next;
						obj;
				};
			}
		};
	}
	public function criterator():Iterator<T>
	{
		var myTailEnd = tailEnd;
		var next = tailEnd;
		var circular = false;
		return {
			hasNext: function() {
				return (next != null || !circular);
			},
			next: function() {
				return switch (next) {
					case null:
						circular = true;
						switch (myTailEnd) {
							case null: null;
							default: myTailEnd.obj;
						}
					default:
						var obj = next.obj;
						next = next.previous;
						obj;
				};
			}
		};
	}
#end
/*
	public static function test()
	{
		var a = new DLList<Int>();
		trace("push");
		Lambda.iter([1, 2, 3, 4, 5, 6], function (__cur) {
			a.push(__cur);
		});
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("----------");
		trace("pop");
		trace(a.pop());
		trace(a.pop());
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace(a.pop()); trace(a.pop()); trace(a.pop()); trace(a.pop());
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace(a.pop());
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("push");
		Lambda.iter([1, 2, 3, 4, 5, 6], function (__cur) {
			a.push(__cur);
		});
		trace("remove ");
		trace("remove 4 "+a.remove(4));
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("remove 1 "+a.remove(1));
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("remove 6 "+a.remove(6));
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("remove 9 "+a.remove(9));
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("pop "+a.pop());
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("remove 3 "+a.remove(3));
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("remove 2 "+a.remove(2));
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		trace("remove 9 "+a.remove(9));
		trace(a);
		trace(a.head);
		trace(a.tailEnd);
		var ltrace = function (a:DLList<Int>) { trace(a); trace(a.head); trace(a.tailEnd); };
		trace("push 2312"); var aa = a.mpush(2312); ltrace(a);
		trace("remove by ptr 2312 " + a.mremove(aa)); ltrace(a);
		trace("add");
		Lambda.iter([1, 2, 3, 4, 5, 6], function (__cur) {
			a.add(__cur);
		});
		ltrace(a);
		trace("circular iterator");
		for (aaa in a.citerator()) {
			trace(aaa);
		}
		trace("insert 9 after 3");
		aa = a.mfind(3); trace(aa);
		trace(a.minsertAfter(9, aa));
		ltrace(a);
		trace("insert 9 after 6");
		aa = a.mfind(6); trace(aa);
		trace(a.minsertAfter(9, aa));
		ltrace(a);
		trace("insert 10 after tail");
		aa = a.tailEnd; trace(aa);
		trace(a.minsertAfter(10, aa));
		ltrace(a);
		trace("map function(cur) return cur*2;");
		var b = a.omap(function(cur) { return cur * 2; } );
		ltrace(b);
		trace("circular iterator");
		for (bbb in b.citerator()) {
			trace(bbb);
		}
		trace("filter > 7");
		var c = b.filter(function(cur) { if (cur > 7) return true; else return false; } );
		ltrace(c);
		trace("concat a b");
		var d = a.concat(b); ltrace(d);
		trace("circular reverse iterator");
		for (ddd in d.criterator()) {
			trace(ddd);
		}
		trace("insert 8231 before 9");
		var dd = d.mfind(9); trace(dd);
		trace(d.minsertBefore(8231, dd));
		ltrace(d);
		trace("insert 2312 before head");
		trace(d.minsertBefore(2312, d.head));
		ltrace(d);
		
	}
*/
}

class DLListNode<T>
{
	public var next:DLListNode<T>;
	public var previous:DLListNode<T>;
	public var obj:T;
	public inline function new(obj:T, previous:DLListNode<T>, next:DLListNode<T>)
	{
		this.obj = obj;
		this.previous = previous;
		this.next = next;
	}
	/**
	 * Clears all properties
	 */
	public inline function clear()
	{
		this.obj = null;
		this.next = null;
		this.next = null;
	}
	/**
	 * Clears all properties and returns the pointer for the next node
	 */
	public inline function mkill()
	{
		var __mkill__dead = { pre: this.previous, nx: this.next };
		this.obj = null;
		this.next = null;
		this.previous = null;
		return __mkill__dead;
	}
	
	public function toString()
	{
		var __temp__s = new StringBuf();
		__temp__s.add("{obj: ");
		__temp__s.add(Std.string(obj));
		__temp__s.add(", previous: ");
		__temp__s.add(Std.string(switch (previous) { case null: null; default: previous.obj; } ));
		__temp__s.add(", next: ");
		__temp__s.add(Std.string(switch (next) { case null: null; default: next.obj; } ));
		__temp__s.add("}");
		return __temp__s.toString();
	}
}

#if cpp
class DLListIterator<T> extends cpp.FastIterator<T>
{
	var nx:DLListNode<T>;
	override public function hasNext():Bool { return nx != null; }
	override public function next():T {
			var obj = nx.obj;
			nx = nx.next;
			return obj; }
	public function new(head) { nx = head; }
}
class DLListRIterator<T> extends cpp.FastIterator<T>
{
	var nx:DLListNode<T>;
	override public function hasNext():Bool { return nx != null; }
	override public function next():T {
			var obj = nx.obj;
			nx = nx.previous;
			return obj; }
	public function new(tailEnd) { nx = tailEnd; }
}
class DLListCIterator<T> extends cpp.FastIterator<T>
{
	var hd:DLListNode<T>;
	var nx:DLListNode<T>;
	var circular:Bool;
	override public function hasNext():Bool { return (nx != null || !circular); }
	override public function next():T {
		return switch (nx) {
			case null:
				circular = true;
				switch (hd) {
					case null: null;
					default: hd.obj;
				}
			default:
				var obj = nx.obj;
				nx = nx.next;
				obj;
		}; }
	public function new(head)
	{
		hd = head;
		nx = head;
		circular = false;
	}
}
class DLListCRIterator<T> extends cpp.FastIterator<T>
{
	var hd:DLListNode<T>;
	var nx:DLListNode<T>;
	var circular:Bool;
	override public function hasNext():Bool { return (nx != null || !circular); }
	override public function next():T {
		return switch (nx) {
			case null:
				circular = true;
				switch (hd) {
					case null: null;
					default: hd.obj;
				}
			default:
				var obj = nx.obj;
				nx = nx.previous;
				obj;
		}; }
	public function new(tailEnd)
	{
		hd = tailEnd;
		nx = tailEnd;
		circular = false;
	}
}
#end